﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using ConsoleApplication1.RuntimeAutofac;

namespace ConsoleApplication1
{
    public interface IOutput
    {
        void Write(string content);
    }

    public class ConsoleOutput : IOutput
    {
        public void Write(string content)
        {
            Console.WriteLine(content);
        }
    }

    public interface IDateWiter
    {
        void WriteDate();
    }

    public class ToDayWriter : IDateWiter
    {
        private IOutput _output;

        public ToDayWriter(IOutput output)
        {
            _output = output;
        }

        public void WriteDate()
        {
            _output.Write(DateTime.Today.ToShortDateString());
        }
    }

    class Program
    {
        private static IContainer Container { get; set; }

        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            //builder.RegisterType<ConsoleOutput>().As<IOutput>();
            //builder.RegisterType<ToDayWriter>().As<IDateWiter>();

            // Assembly scanning
            var dataAccess = Assembly.GetExecutingAssembly();
            builder.RegisterAssemblyTypes(dataAccess).AsImplementedInterfaces().AsSelf(); // .AsSelf() method register type as a type (test with IHomeControler)

            // Register runtime using autofac
            //RegisterRuntimeFirst(builder);

            // Register runtime using Keyed Registration
            RegisterRuntimeSecond(builder);

            Container = builder.Build();

            WriteDate();

            // Autofac register
            RuntimeReg();

            Console.WriteLine("Press any key...");
            Console.ReadLine();
        }

        private static void WriteDate()
        {
            using (var scope = Container.BeginLifetimeScope())
            {
                var writer = scope.Resolve<IDateWiter>();
                writer.WriteDate();
            }
        }

        // First version may have services with its own dependency
        private static void RegisterRuntimeFirst(ContainerBuilder builder)
        {
            builder.Register<Func<RoleName, IDataService>>(c => (roleName) =>
                                                                {
                                                                    switch (roleName)
                                                                    {
                                                                        case RoleName.Premium:
                                                                            return new PremiumDataService();
                                                                        case RoleName.Normal:
                                                                            return new NormalDataService();
                                                                        default:
                                                                            throw new ArgumentException();
                                                                    }
                                                                });
        }

        // Second version, autofac will auto resolve services's dependency
        public static void RegisterRuntimeSecond(ContainerBuilder builder)
        {
            /*
                > RegisterType allows Type parameter
                Simple class with no parameter in the constructor:
	                builder.RegisterType<ConsoleOutputService>().As<IOutputService>();
                As explained in the tutorial, the code above can be read as: setup ConsoleOutputService as the implementation of IOutputService
                > Register allows delegate parameter so that can put parameters
                Simple class with one parameter in the constructor:
	                builder.Register(c => new MultipleOutputService(outputFilePath)).As<IOutputService>();
                ==> You can't write that code because it doesn't make sense in C#.
                RegisterType is a generic method; generic methods must take types as generic parameters.

                You're trying to register a type with a custom way to create it (inyour case, a constructor parameter); the only way that C# supports to specify such a thing is a lambda expression (or other delegate).
            */

            builder.RegisterType<PremiumDataService>().As<IDataService>().Keyed<IDataService>(RoleName.Premium);
            builder.RegisterType<NormalDataService>().As<IDataService>().Keyed<IDataService>(RoleName.Normal);

            builder.Register<Func<RoleName, IDataService>>(c =>
                                                           {
                                                               var componentContext = c.Resolve<IComponentContext>();
                                                               return (rolename) =>
                                                                      {
                                                                          var dataService =
                                                                              componentContext
                                                                                  .ResolveKeyed<IDataService>(rolename);
                                                                          return dataService;
                                                                      };
                                                           });
        }

        private static void RuntimeReg()
        {
            using (var scope = Container.BeginLifetimeScope())
            {
                var service = scope.Resolve<IHomeControler>();
                service.GetData(RoleName.Premium);
                service.GetData(RoleName.Normal);
            }
        }
    }
}
